home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / shells / sh03src.zoo / sh-pl03 / sh / expand.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-23  |  22.1 KB  |  1,119 lines

  1. /*-
  2.  * Copyright (c) 1991 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * This code is derived from software contributed to Berkeley by
  6.  * Kenneth Almquist.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  * 1. Redistributions of source code must retain the above copyright
  12.  *    notice, this list of conditions and the following disclaimer.
  13.  * 2. Redistributions in binary form must reproduce the above copyright
  14.  *    notice, this list of conditions and the following disclaimer in the
  15.  *    documentation and/or other materials provided with the distribution.
  16.  * 3. All advertising materials mentioning features or use of this software
  17.  *    must display the following acknowledgement:
  18.  *    This product includes software developed by the University of
  19.  *    California, Berkeley and its contributors.
  20.  * 4. Neither the name of the University nor the names of its contributors
  21.  *    may be used to endorse or promote products derived from this software
  22.  *    without specific prior written permission.
  23.  *
  24.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  25.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  28.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34.  * SUCH DAMAGE.
  35.  */
  36.  
  37. #ifndef lint
  38. static char sccsid[] = "@(#)expand.c    5.1 (Berkeley) 3/7/91";
  39. #endif /* not lint */
  40.  
  41. /*
  42.  * Routines to expand arguments to commands.  We have to deal with
  43.  * backquotes, shell variables, and file metacharacters.
  44.  */
  45.  
  46. #include "shell.h"
  47. #include "main.h"
  48. #include "nodes.h"
  49. #include "eval.h"
  50. #include "expand.h"
  51. #include "syntax.h"
  52. #include "parser.h"
  53. #include "jobs.h"
  54. #include "options.h"
  55. #include "var.h"
  56. #include "input.h"
  57. #include "output.h"
  58. #include "memalloc.h"
  59. #include "error.h"
  60. #include "mystring.h"
  61. #include <sys/types.h>
  62. #include <sys/stat.h>
  63. #include <errno.h>
  64. #include <dirent.h>
  65.  
  66. /*
  67.  * Structure specifying which parts of the string should be searched
  68.  * for IFS characters.
  69.  */
  70.  
  71. struct ifsregion {
  72.     struct ifsregion *next;    /* next region in list */
  73.     int begoff;        /* offset of start of region */
  74.     int endoff;        /* offset of end of region */
  75.     int nulonly;        /* search for nul bytes only */
  76. };
  77.  
  78.  
  79. char *expdest;            /* output of current string */
  80. struct nodelist *argbackq;    /* list of back quote expressions */
  81. struct ifsregion ifsfirst;    /* first struct in list of ifs regions */
  82. struct ifsregion *ifslastp;    /* last struct in list */
  83. struct arglist exparg;        /* holds expanded arg list */
  84. #if UDIR
  85. /*
  86.  * Set if the last argument processed had /u/logname expanded.  This
  87.  * variable is read by the cd command.
  88.  */
  89. int didudir;
  90. #endif
  91.  
  92. #ifdef __STDC__
  93. STATIC void argstr(char *, int);
  94. STATIC void expbackq(union node *, int, int);
  95. STATIC char *evalvar(char *, int);
  96. STATIC int varisset(int);
  97. STATIC void varvalue(int, int, int);
  98. STATIC void recordregion(int, int, int);
  99. STATIC void ifsbreakup(char *, struct arglist *);
  100. STATIC void expandmeta(struct strlist *);
  101. STATIC void expmeta(char *, char *);
  102. STATIC void addfname(char *);
  103. STATIC struct strlist *expsort(struct strlist *);
  104. STATIC struct strlist *msort(struct strlist *, int);
  105. STATIC int pmatch(char *, char *);
  106. #else
  107. STATIC void argstr();
  108. STATIC void expbackq();
  109. STATIC char *evalvar();
  110. STATIC int varisset();
  111. STATIC void varvalue();
  112. STATIC void recordregion();
  113. STATIC void ifsbreakup();
  114. STATIC void expandmeta();
  115. STATIC void expmeta();
  116. STATIC void addfname();
  117. STATIC struct strlist *expsort();
  118. STATIC struct strlist *msort();
  119. STATIC int pmatch();
  120. #endif
  121. #if UDIR
  122. #ifdef __STDC__
  123. STATIC char *expudir(char *);
  124. #else
  125. STATIC char *expudir();
  126. #endif
  127. #endif /* UDIR */
  128.  
  129.  
  130.  
  131. /*
  132.  * Expand shell variables and backquotes inside a here document.
  133.  */
  134.  
  135. void
  136. expandhere(arg, fd)
  137.     union node *arg;    /* the document */
  138.     int fd;            /* where to write the expanded version */
  139.     {
  140.     herefd = fd;
  141.     expandarg(arg, (struct arglist *)NULL, 0);
  142.     xwrite(fd, stackblock(), expdest - stackblock());
  143. }
  144.  
  145.  
  146. /*
  147.  * Perform variable substitution and command substitution on an argument,
  148.  * placing the resulting list of arguments in arglist.  If full is true,
  149.  * perform splitting and file name expansion.  When arglist is NULL, perform
  150.  * here document expansion.
  151.  */
  152.  
  153. void
  154. expandarg(arg, arglist, full)
  155.     union node *arg;
  156.     struct arglist *arglist;
  157.     {
  158.     struct strlist *sp;
  159.     char *p;
  160.  
  161. #if UDIR
  162.     didudir = 0;
  163. #endif
  164.     argbackq = arg->narg.backquote;
  165.     STARTSTACKSTR(expdest);
  166.     ifsfirst.next = NULL;
  167.     ifslastp = NULL;
  168.     argstr(arg->narg.text, full);
  169.     if (arglist == NULL)
  170.         return;            /* here document expanded */
  171.     STPUTC('\0', expdest);
  172.     p = grabstackstr(expdest);
  173.     exparg.lastp = &exparg.list;
  174.     if (full) {
  175.         ifsbreakup(p, &exparg);
  176.         *exparg.lastp = NULL;
  177.         exparg.lastp = &exparg.list;
  178.         expandmeta(exparg.list);
  179.     } else {
  180.         sp = (struct strlist *)stalloc(sizeof (struct strlist));
  181.         sp->text = p;
  182.         *exparg.lastp = sp;
  183.         exparg.lastp = &sp->next;
  184.     }
  185.     while (ifsfirst.next != NULL) {
  186.         struct ifsregion *ifsp;
  187.         INTOFF;
  188.         ifsp = ifsfirst.next->next;
  189.         ckfree(ifsfirst.next);
  190.         ifsfirst.next = ifsp;
  191.         INTON;
  192.     }
  193.     *exparg.lastp = NULL;
  194.     if (exparg.list) {
  195.         *arglist->lastp = exparg.list;
  196.         arglist->lastp = exparg.lastp;
  197.     }
  198. }
  199.  
  200.  
  201.  
  202. /*
  203.  * Perform variable and command substitution.  If full is set, output CTLESC
  204.  * characters to allow for further processing.  If full is not set, treat
  205.  * $@ like $* since no splitting will be performed.
  206.  */
  207.  
  208. STATIC void
  209. argstr(p, full)
  210.     register char *p;
  211.     {
  212.     char c;
  213.  
  214.     for (;;) {
  215.         switch (c = *p++) {
  216.         case '\0':
  217.         case CTLENDVAR:
  218.             goto breakloop;
  219.         case CTLESC:
  220.             if (full)
  221.                 STPUTC(c, expdest);
  222.             c = *p++;
  223.             STPUTC(c, expdest);
  224.             break;
  225.         case CTLVAR:
  226.             p = evalvar(p, full);
  227.             break;
  228.         case CTLBACKQ:
  229.         case CTLBACKQ|CTLQUOTE:
  230.             expbackq(argbackq->n, c & CTLQUOTE, full);
  231.             argbackq = argbackq->next;
  232.             break;
  233.         default:
  234.             STPUTC(c, expdest);
  235.         }
  236.     }
  237. breakloop:;
  238. }
  239.  
  240.  
  241. /*
  242.  * Expand stuff in backwards quotes.
  243.  */
  244.  
  245. STATIC void
  246. expbackq(cmd, quoted, full)
  247.     union node *cmd;
  248.     {
  249.     struct backcmd in;
  250.     int i;
  251.     char buf[128];
  252.     char *p;
  253.     char *dest = expdest;
  254.     struct ifsregion saveifs, *savelastp;
  255.     struct nodelist *saveargbackq;
  256.     char lastc;
  257.     int startloc = dest - stackblock();
  258.     char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
  259.     int saveherefd;
  260.  
  261.     INTOFF;
  262.     saveifs = ifsfirst;
  263.     savelastp = ifslastp;
  264.     saveargbackq = argbackq;
  265.     saveherefd = herefd;      
  266.     herefd = -1;
  267.     p = grabstackstr(dest);
  268.     evalbackcmd(cmd, &in);
  269.     ungrabstackstr(p, dest);
  270.     ifsfirst = saveifs;
  271.     ifslastp = savelastp;
  272.     argbackq = saveargbackq;
  273.     herefd = saveherefd;
  274.  
  275.     p = in.buf;
  276.     lastc = '\0';
  277.     for (;;) {
  278.         if (--in.nleft < 0) {
  279.             if (in.fd < 0)
  280.                 break;
  281.             while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
  282.             TRACE(("expbackq: read returns %d\n", i));
  283.             if (i <= 0)
  284.                 break;
  285.             p = buf;
  286.             in.nleft = i - 1;
  287.         }
  288.         lastc = *p++;
  289. #ifdef __MINT__
  290.         if (lastc != '\0' && lastc != '\r') {
  291. #else
  292.         if (lastc != '\0') {
  293. #endif
  294.             if (full && syntax[lastc] == CCTL)
  295.                 STPUTC(CTLESC, dest);
  296.             STPUTC(lastc, dest);
  297.         }
  298.     }
  299. #ifdef __MINT__
  300.     if (lastc == '\n' || lastc == '\r') {
  301. #else
  302.     if (lastc == '\n') {
  303. #endif
  304.         STUNPUTC(dest);
  305.     }
  306.     if (in.fd >= 0)
  307.         close(in.fd);
  308.     if (in.buf)
  309.         ckfree(in.buf);
  310.     if (in.jp)
  311.         exitstatus = waitforjob(in.jp);
  312.     if (quoted == 0)
  313.         recordregion(startloc, dest - stackblock(), 0);
  314.     TRACE(("evalbackq: size=%d: \"%.*s\"\n",
  315.         (dest - stackblock()) - startloc,
  316.         (dest - stackblock()) - startloc,
  317.         stackblock() + startloc));
  318.     expdest = dest;
  319.     INTON;
  320. }
  321.  
  322.  
  323.  
  324. /*
  325.  * Expand a variable, and return a pointer to the next character in the
  326.  * input string.
  327.  */
  328.  
  329. STATIC char *
  330. evalvar(p, full)
  331.     char *p;
  332.     {
  333.     int subtype;
  334.     int flags;
  335.     char *var;
  336.     char *val;
  337.     int c;
  338.     int set;
  339.     int special;
  340.     int startloc;
  341.  
  342.     flags = *p++;
  343.     subtype = flags & VSTYPE;
  344.     var = p;
  345.     special = 0;
  346.     if (! is_name(*p))
  347.         special = 1;
  348.     p = strchr(p, '=') + 1;
  349. again: /* jump here after setting a variable with ${var=text} */
  350.     if (special) {
  351.         set = varisset(*var);
  352.         val = NULL;
  353.     } else {
  354.         val = lookupvar(var);
  355.         if (val == NULL || (flags & VSNUL) && val[0] == '\0') {
  356.             val = NULL;
  357.             set = 0;
  358.         } else
  359.             set = 1;
  360.     }
  361.     startloc = expdest - stackblock();
  362.     if (set && subtype != VSPLUS) {
  363.         /* insert the value of the variable */
  364.         if (special) {
  365.             varvalue(*var, flags & VSQUOTE, full);
  366.         } else {
  367.             char const *syntax = (flags & VSQUOTE)? DQSYNTAX : BASESYNTAX;
  368.  
  369.             while (*val) {
  370.                 if (full && syntax[*val] == CCTL)
  371.                     STPUTC(CTLESC, expdest);
  372.                 STPUTC(*val++, expdest);
  373.             }
  374.         }
  375.     }
  376.     if (subtype == VSPLUS)
  377.         set = ! set;
  378.     if (((flags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1))
  379.      && (set || subtype == VSNORMAL))
  380.         recordregion(startloc, expdest - stackblock(), flags & VSQUOTE);
  381.     if (! set && subtype != VSNORMAL) {
  382.         if (subtype == VSPLUS || subtype == VSMINUS) {
  383.             argstr(p, full);
  384.         } else {
  385.             char *startp;
  386.             int saveherefd = herefd;
  387.             herefd = -1;
  388.             argstr(p, 0);
  389.             STACKSTRNUL(expdest);
  390.             herefd = saveherefd;
  391.             startp = stackblock() + startloc;
  392.             if (subtype == VSASSIGN) {
  393.                 setvar(var, startp, 0);
  394.                 STADJUST(startp - expdest, expdest);
  395.                 flags &=~ VSNUL;
  396.                 goto again;
  397.             }
  398.             /* subtype == VSQUESTION */
  399.             if (*p != CTLENDVAR) {
  400.                 outfmt(&errout, "%s\n", startp);
  401.                 error((char *)NULL);
  402.             }
  403.             error("%.*s: parameter %snot set", p - var - 1,
  404.                 var, (flags & VSNUL)? "null or " : nullstr);
  405.         }
  406.     }
  407.     if (subtype != VSNORMAL) {    /* skip to end of alternative */
  408.         int nesting = 1;
  409.         for (;;) {
  410.             if ((c = *p++) == CTLESC)
  411.                 p++;
  412.             else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
  413.                 if (set)
  414.                     argbackq = argbackq->next;
  415.             } else if (c == CTLVAR) {
  416.                 if ((*p++ & VSTYPE) != VSNORMAL)
  417.                     nesting++;
  418.             } else if (c == CTLENDVAR) {
  419.                 if (--nesting == 0)
  420.                     break;
  421.             }
  422.         }
  423.     }
  424.     return p;
  425. }
  426.  
  427.  
  428.  
  429. /*
  430.  * Test whether a specialized variable is set.
  431.  */
  432.  
  433. STATIC int
  434. varisset(name)
  435.     char name;
  436.     {
  437.     char **ap;
  438.  
  439.     if (name == '!') {
  440.         if (backgndpid == -1)
  441.             return 0;
  442.     } else if (name == '@' || name == '*') {
  443.         if (*shellparam.p == NULL)
  444.             return 0;
  445.     } else if ((unsigned)(name -= '1') <= '9' - '1') {
  446.         ap = shellparam.p;
  447.         do {
  448.             if (*ap++ == NULL)
  449.                 return 0;
  450.         } while (--name >= 0);
  451.     }
  452.     return 1;
  453. }
  454.  
  455.  
  456.  
  457. /*
  458.  * Add the value of a specialized variable to the stack string.
  459.  */
  460.  
  461. STATIC void
  462. varvalue(name, quoted, allow_split)
  463.     char name;
  464.     {
  465.     int num;
  466.     char temp[32];
  467.     char *p;
  468.     int i;
  469.     extern int exitstatus;
  470.     char sep;
  471.     char **ap;
  472.     char const *syntax;
  473.  
  474.     switch (name) {
  475.     case '$':
  476.         num = rootpid;
  477.         goto numvar;
  478.     case '?':
  479.         num = exitstatus;
  480.         goto numvar;
  481.     case '#':
  482.         num = shellparam.nparam;
  483.         goto numvar;
  484.     case '!':
  485.         num = backgndpid;
  486. numvar:
  487.         p = temp + 31;
  488.         temp[31] = '\0';
  489.         do {
  490.             *--p = num % 10 + '0';
  491.         } while ((num /= 10) != 0);
  492.         while (*p)
  493.             STPUTC(*p++, expdest);
  494.         break;
  495.     case '-':
  496.         for (i = 0 ; optchar[i] ; i++) {
  497.             if (optval[i])
  498.                 STPUTC(optchar[i], expdest);
  499.         }
  500.         break;
  501.     case '@':
  502.         if (allow_split) {
  503.             sep = '\0';
  504.             goto allargs;
  505.         }
  506.         /* fall through */            
  507.     case '*':
  508.         sep = ' ';
  509. allargs:
  510.         /* Only emit CTLESC if we will do further processing,
  511.            i.e. if allow_split is set.  */
  512.         syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX;
  513.         for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
  514.             /* should insert CTLESC characters */
  515.             while (*p) {
  516.                 if (syntax[*p] == CCTL)
  517.                     STPUTC(CTLESC, expdest);
  518.                 STPUTC(*p++, expdest);
  519.             }
  520.             if (*ap)
  521.                 STPUTC(sep, expdest);
  522.         }
  523.         break;
  524.     case '0':
  525.         p = arg0;
  526. string:
  527.         /* Only emit CTLESC if we will do further processing,
  528.            i.e. if allow_split is set.  */
  529.         syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX;
  530.         while (*p) {
  531.             if (syntax[*p] == CCTL)
  532.                 STPUTC(CTLESC, expdest);
  533.             STPUTC(*p++, expdest);
  534.         }
  535.         break;
  536.     default:
  537.         if ((unsigned)(name -= '1') <= '9' - '1') {
  538.             p = shellparam.p[name];
  539.             goto string;
  540.         }
  541.         break;
  542.     }
  543. }
  544.  
  545.  
  546.  
  547. /*
  548.  * Record the the fact that we have to scan this region of the
  549.  * string for IFS characters.
  550.  */
  551.  
  552. STATIC void
  553. recordregion(start, end, nulonly) {
  554.     register struct ifsregion *ifsp;
  555.  
  556.     if (ifslastp == NULL) {
  557.         ifsp = &ifsfirst;
  558.     } else {
  559.         ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
  560.         ifslastp->next = ifsp;
  561.     }
  562.     ifslastp = ifsp;
  563.     ifslastp->next = NULL;
  564.     ifslastp->begoff = start;
  565.     ifslastp->endoff = end;
  566.     ifslastp->nulonly = nulonly;
  567. }
  568.  
  569.  
  570.  
  571. /*
  572.  * Break the argument string into pieces based upon IFS and add the
  573.  * strings to the argument list.  The regions of the string to be
  574.  * searched for IFS characters have been stored by recordregion.
  575.  */
  576.  
  577. STATIC void
  578. ifsbreakup(string, arglist)
  579.     char *string;
  580.     struct arglist *arglist;
  581.     {
  582.     struct ifsregion *ifsp;
  583.     struct strlist *sp;
  584.     char *start;
  585.     register char *p;
  586.     char *q;
  587.     char *ifs;
  588.  
  589.     start = string;
  590.     if (ifslastp != NULL) {
  591.         ifsp = &ifsfirst;
  592.         do {
  593.             p = string + ifsp->begoff;
  594.             ifs = ifsp->nulonly? nullstr : ifsval();
  595.             while (p < string + ifsp->endoff) {
  596.                 q = p;
  597.                 if (*p == CTLESC)
  598.                     p++;
  599.                 if (strchr(ifs, *p++)) {
  600.                     if (q > start || *ifs != ' ') {
  601.                         *q = '\0';
  602.                         sp = (struct strlist *)stalloc(sizeof *sp);
  603.                         sp->text = start;
  604.                         *arglist->lastp = sp;
  605.                         arglist->lastp = &sp->next;
  606.                     }
  607.                     if (*ifs == ' ') {
  608.                         for (;;) {
  609.                             if (p >= string + ifsp->endoff)
  610.                                 break;
  611.                             q = p;
  612.                             if (*p == CTLESC)
  613.                                 p++;
  614.                             if (strchr(ifs, *p++) == NULL) {
  615.                                 p = q;
  616.                                 break;
  617.                             }
  618.                         }
  619.                     }
  620.                     start = p;
  621.                 }
  622.             }
  623.         } while ((ifsp = ifsp->next) != NULL);
  624.         if (*start || (*ifs != ' ' && start > string)) {
  625.             sp = (struct strlist *)stalloc(sizeof *sp);
  626.             sp->text = start;
  627.             *arglist->lastp = sp;
  628.             arglist->lastp = &sp->next;
  629.         }
  630.     } else {
  631.         sp = (struct strlist *)stalloc(sizeof *sp);
  632.         sp->text = start;
  633.         *arglist->lastp = sp;
  634.         arglist->lastp = &sp->next;
  635.     }
  636. }
  637.  
  638.  
  639.  
  640. /*
  641.  * Expand shell metacharacters.  At this point, the only control characters
  642.  * should be escapes.  The results are stored in the list exparg.
  643.  */
  644.  
  645. char *expdir;
  646.  
  647.  
  648. STATIC void
  649. expandmeta(str)
  650.     struct strlist *str;
  651.     {
  652.     char *p;
  653.     struct strlist **savelastp;
  654.     struct strlist *sp;
  655.     char c;
  656.  
  657.     while (str) {
  658.         if (fflag)
  659.             goto nometa;
  660.         p = str->text;
  661. #if UDIR
  662.         if (p[0] == '/' && p[1] == 'u' && p[2] == '/')
  663.             str->text = p = expudir(p);
  664. #endif
  665.         for (;;) {            /* fast check for meta chars */
  666.             if ((c = *p++) == '\0')
  667.                 goto nometa;
  668.             if (c == '*' || c == '?' || c == '[' || c == '!')
  669.                 break;
  670.         }
  671.         savelastp = exparg.lastp;
  672.         INTOFF;
  673.         if (expdir == NULL)
  674.             expdir = ckmalloc(1024); /* I hope this is big enough */
  675.         expmeta(expdir, str->text);
  676.         ckfree(expdir);
  677.         expdir = NULL;
  678.         INTON;
  679.         if (exparg.lastp == savelastp) {
  680.             if (! zflag) {
  681. nometa:
  682.                 *exparg.lastp = str;
  683.                 rmescapes(str->text);
  684.                 exparg.lastp = &str->next;
  685.             }
  686.         } else {
  687.             *exparg.lastp = NULL;
  688.             *savelastp = sp = expsort(*savelastp);
  689.             while (sp->next != NULL)
  690.                 sp = sp->next;
  691.             exparg.lastp = &sp->next;
  692.         }
  693.         str = str->next;
  694.     }
  695. }
  696.  
  697.  
  698. #if UDIR
  699. /*
  700.  * Expand /u/username into the home directory for the specified user.
  701.  * We could use the getpw stuff here, but then we would have to load
  702.  * in stdio and who knows what else.
  703.  */
  704.  
  705. #define MAXLOGNAME 32
  706. #define MAXPWLINE 128
  707.  
  708. char *pfgets();
  709.  
  710.  
  711. STATIC char *
  712. expudir(path)
  713.     char *path;
  714.     {
  715.     register char *p, *q, *r;
  716.     char name[MAXLOGNAME];
  717.     char line[MAXPWLINE];
  718.     int i;
  719.  
  720.     r = path;                /* result on failure */
  721.     p = r + 3;            /* the 3 skips "/u/" */
  722.     q = name;
  723.     while (*p && *p != '/') {
  724.         if (q >= name + MAXLOGNAME - 1)
  725.             return r;        /* fail, name too long */
  726.         *q++ = *p++;
  727.     }
  728.     *q = '\0';
  729.     setinputfile("/etc/passwd", 1);
  730.     q = line + strlen(name);
  731.     while (pfgets(line, MAXPWLINE) != NULL) {
  732.         if (line[0] == name[0] && prefix(name, line) && *q == ':') {
  733.             /* skip to start of home directory */
  734.             i = 4;
  735.             do {
  736.                 while (*++q && *q != ':');
  737.             } while (--i > 0);
  738.             if (*q == '\0')
  739.                 break;        /* fail, corrupted /etc/passwd */
  740.             q++;
  741.             for (r = q ; *r && *r != '\n' && *r != ':' ; r++);
  742.             *r = '\0';        /* nul terminate home directory */
  743.             i = r - q;        /* i = strlen(q) */
  744.             r = stalloc(i + strlen(p) + 1);
  745.             scopy(q, r);
  746.             scopy(p, r + i);
  747.             TRACE(("expudir converts %s to %s\n", path, r));
  748.             didudir = 1;
  749.             path = r;        /* succeed */
  750.             break;
  751.         }
  752.     }
  753.     popfile();
  754.     return r;
  755. }
  756. #endif
  757.  
  758.  
  759. /*
  760.  * Do metacharacter (i.e. *, ?, [...]) expansion.
  761.  */
  762.  
  763. STATIC void
  764. expmeta(enddir, name)
  765.     char *enddir;
  766.     char *name;
  767.     {
  768.     register char *p;
  769.     char *q;
  770.     char *start;
  771.     char *endname;
  772.     int metaflag;
  773.     struct stat statb;
  774.     DIR *dirp;
  775.     struct dirent *dp;
  776.     int atend;
  777.     int matchdot;
  778.  
  779.     metaflag = 0;
  780.     start = name;
  781.     for (p = name ; ; p++) {
  782.         if (*p == '*' || *p == '?')
  783.             metaflag = 1;
  784.         else if (*p == '[') {
  785.             q = p + 1;
  786.             if (*q == '!')
  787.                 q++;
  788.             for (;;) {
  789.                 if (*q == CTLESC)
  790.                     q++;
  791.                 if (*q == '/' || *q == '\0')
  792.                     break;
  793.                 if (*++q == ']') {
  794.                     metaflag = 1;
  795.                     break;
  796.                 }
  797.             }
  798.         } else if (*p == '!' && p[1] == '!'    && (p == name || p[-1] == '/')) {
  799.             metaflag = 1;
  800.         } else if (*p == '\0')
  801.             break;
  802.         else if (*p == CTLESC)
  803.             p++;
  804.         if (*p == '/') {
  805.             if (metaflag)
  806.                 break;
  807.             start = p + 1;
  808.         }
  809.     }
  810.     if (metaflag == 0) {    /* we've reached the end of the file name */
  811.         if (enddir != expdir)
  812.             metaflag++;
  813.         for (p = name ; ; p++) {
  814.             if (*p == CTLESC)
  815.                 p++;
  816.             *enddir++ = *p;
  817.             if (*p == '\0')
  818.                 break;
  819.         }
  820.         if (metaflag == 0 || stat(expdir, &statb) >= 0)
  821.             addfname(expdir);
  822.         return;
  823.     }
  824.     endname = p;
  825.     if (start != name) {
  826.         p = name;
  827.         while (p < start) {
  828.             if (*p == CTLESC)
  829.                 p++;
  830.             *enddir++ = *p++;
  831.         }
  832.     }
  833.     if (enddir == expdir) {
  834.         p = ".";
  835.     } else if (enddir == expdir + 1 && *expdir == '/') {
  836.         p = "/";
  837.     } else {
  838.         p = expdir;
  839.         enddir[-1] = '\0';
  840.     }
  841.     if ((dirp = opendir(p)) == NULL)
  842.         return;
  843.     if (enddir != expdir)
  844.         enddir[-1] = '/';
  845.     if (*endname == 0) {
  846.         atend = 1;
  847.     } else {
  848.         atend = 0;
  849.         *endname++ = '\0';
  850.     }
  851.     matchdot = 0;
  852.     if (start[0] == '.' || start[0] == CTLESC && start[1] == '.')
  853.         matchdot++;
  854.     while (! int_pending() && (dp = readdir(dirp)) != NULL) {
  855.         if (dp->d_name[0] == '.' && ! matchdot)
  856.             continue;
  857.         if (patmatch(start, dp->d_name)) {
  858.             if (atend) {
  859.                 scopy(dp->d_name, enddir);
  860.                 addfname(expdir);
  861.             } else {
  862.                 char *q;
  863.                 for (p = enddir, q = dp->d_name ; *p++ = *q++ ;);
  864.                 p[-1] = '/';
  865.                 expmeta(p, endname);
  866.             }
  867.         }
  868.     }
  869.     closedir(dirp);
  870.     if (! atend)
  871.         endname[-1] = '/';
  872. }
  873.  
  874.  
  875. /*
  876.  * Add a file name to the list.
  877.  */
  878.  
  879. STATIC void
  880. addfname(name)
  881.     char *name;
  882.     {
  883.     char *p;
  884.     struct strlist *sp;
  885.  
  886.     p = stalloc(strlen(name) + 1);
  887.     scopy(name, p);
  888.     sp = (struct strlist *)stalloc(sizeof *sp);
  889.     sp->text = p;
  890.     *exparg.lastp = sp;
  891.     exparg.lastp = &sp->next;
  892. }
  893.  
  894.  
  895. /*
  896.  * Sort the results of file name expansion.  It calculates the number of
  897.  * strings to sort and then calls msort (short for merge sort) to do the
  898.  * work.
  899.  */
  900.  
  901. STATIC struct strlist *
  902. expsort(str)
  903.     struct strlist *str;
  904.     {
  905.     int len;
  906.     struct strlist *sp;
  907.  
  908.     len = 0;
  909.     for (sp = str ; sp ; sp = sp->next)
  910.         len++;
  911.     return msort(str, len);
  912. }
  913.  
  914.  
  915. STATIC struct strlist *
  916. msort(list, len)
  917.     struct strlist *list;
  918.     {
  919.     struct strlist *p, *q;
  920.     struct strlist **lpp;
  921.     int half;
  922.     int n;
  923.  
  924.     if (len <= 1)
  925.         return list;
  926.     half = len >> 1;      
  927.     p = list;
  928.     for (n = half ; --n >= 0 ; ) {
  929.         q = p;
  930.         p = p->next;
  931.     }
  932.     q->next = NULL;            /* terminate first half of list */
  933.     q = msort(list, half);        /* sort first half of list */
  934.     p = msort(p, len - half);        /* sort second half */
  935.     lpp = &list;
  936.     for (;;) {
  937.         if (strcmp(p->text, q->text) < 0) {
  938.             *lpp = p;
  939.             lpp = &p->next;
  940.             if ((p = *lpp) == NULL) {
  941.                 *lpp = q;
  942.                 break;
  943.             }
  944.         } else {
  945.             *lpp = q;
  946.             lpp = &q->next;
  947.             if ((q = *lpp) == NULL) {
  948.                 *lpp = p;
  949.                 break;
  950.             }
  951.         }
  952.     }
  953.     return list;
  954. }
  955.  
  956.  
  957.  
  958. /*
  959.  * Returns true if the pattern matches the string.
  960.  */
  961.  
  962. int
  963. patmatch(pattern, string)
  964.     char *pattern;
  965.     char *string;
  966.     {
  967.     if (pattern[0] == '!' && pattern[1] == '!')
  968.         return 1 - pmatch(pattern + 2, string);
  969.     else
  970.         return pmatch(pattern, string);
  971. }
  972.  
  973.  
  974. STATIC int
  975. pmatch(pattern, string)
  976.     char *pattern;
  977.     char *string;
  978.     {
  979.     register char *p, *q;
  980.     register char c;
  981.  
  982.     p = pattern;
  983.     q = string;
  984.     for (;;) {
  985.         switch (c = *p++) {
  986.         case '\0':
  987.             goto breakloop;
  988.         case CTLESC:
  989.             if (*q++ != *p++)
  990.                 return 0;
  991.             break;
  992.         case '?':
  993.             if (*q++ == '\0')
  994.                 return 0;
  995.             break;
  996.         case '*':
  997.             c = *p;
  998.             if (c != CTLESC && c != '?' && c != '*' && c != '[') {
  999.                 while (*q != c) {
  1000.                     if (*q == '\0')
  1001.                         return 0;
  1002.                     q++;
  1003.                 }
  1004.             }
  1005.             do {
  1006.                 if (pmatch(p, q))
  1007.                     return 1;
  1008.             } while (*q++ != '\0');
  1009.             return 0;
  1010.         case '[': {
  1011.             char *endp;
  1012.             int invert, found;
  1013.             char chr;
  1014.  
  1015.             endp = p;
  1016.             if (*endp == '!')
  1017.                 endp++;
  1018.             for (;;) {
  1019.                 if (*endp == '\0')
  1020.                     goto dft;        /* no matching ] */
  1021.                 if (*endp == CTLESC)
  1022.                     endp++;
  1023.                 if (*++endp == ']')
  1024.                     break;
  1025.             }
  1026.             invert = 0;
  1027.             if (*p == '!') {
  1028.                 invert++;
  1029.                 p++;
  1030.             }
  1031.             found = 0;
  1032.             chr = *q++;
  1033.             c = *p++;
  1034.             do {
  1035.                 if (c == CTLESC)
  1036.                     c = *p++;
  1037.                 if (*p == '-' && p[1] != ']') {
  1038.                     p++;
  1039.                     if (*p == CTLESC)
  1040.                         p++;
  1041.                     if (chr >= c && chr <= *p)
  1042.                         found = 1;
  1043.                     p++;
  1044.                 } else {
  1045.                     if (chr == c)
  1046.                         found = 1;
  1047.                 }
  1048.             } while ((c = *p++) != ']');
  1049.             if (found == invert)
  1050.                 return 0;
  1051.             break;
  1052.         }
  1053. dft:        default:
  1054.             if (*q++ != c)
  1055.                 return 0;
  1056.             break;
  1057.         }
  1058.     }
  1059. breakloop:
  1060.     if (*q != '\0')
  1061.         return 0;
  1062.     return 1;
  1063. }
  1064.  
  1065.  
  1066.  
  1067. /*
  1068.  * Remove any CTLESC characters from a string.
  1069.  */
  1070.  
  1071. void
  1072. rmescapes(str)
  1073.     char *str;
  1074.     {
  1075.     register char *p, *q;
  1076.  
  1077.     p = str;
  1078.     while (*p != CTLESC) {
  1079.         if (*p++ == '\0')
  1080.             return;
  1081.     }
  1082.     q = p;
  1083.     while (*p) {
  1084.         if (*p == CTLESC)
  1085.             p++;
  1086.         *q++ = *p++;
  1087.     }
  1088.     *q = '\0';
  1089. }
  1090.  
  1091.  
  1092.  
  1093. /*
  1094.  * See if a pattern matches in a case statement.
  1095.  */
  1096.  
  1097. int
  1098. casematch(pattern, val)
  1099.     union node *pattern;
  1100.     char *val;
  1101.     {
  1102.     struct stackmark smark;
  1103.     int result;
  1104.     char *p;
  1105.  
  1106.     setstackmark(&smark);
  1107.     argbackq = pattern->narg.backquote;
  1108.     STARTSTACKSTR(expdest);
  1109.     ifslastp = NULL;
  1110.     /* Preserve any CTLESC characters inserted previously, so that
  1111.        we won't expand reg exps which are inside strings.  */
  1112.     argstr(pattern->narg.text, 1);
  1113.     STPUTC('\0', expdest);
  1114.     p = grabstackstr(expdest);
  1115.     result = patmatch(p, val);
  1116.     popstackmark(&smark);
  1117.     return result;
  1118. }
  1119.